Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

io/os: Implement IsTerminal trait on Stdio #81807

Closed
wants to merge 4 commits into from

Conversation

ghost
Copy link

@ghost ghost commented Feb 5, 2021

Simple change to use the 'atty' crate in the cli.rs file instead of a custom function. Fixes #80937

However, I couldn't get this to build locally while running ./x.py build --stage 0 and I don't know why. It gives me this error (edited out unrelated parts):

Compiling atty v0.2.14
error[E0463]: can't find crate for core
error: aborting due to previous error
error: could not compile atty

I think this is all that needs to be done here but I'm fairly new to Rust and completely new to contributing to the actual compiler so I'm not sure. Would appreciate any help!

@rust-highfive
Copy link
Collaborator

Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @Mark-Simulacrum (or someone else) soon.

If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes.

Please see the contribution instructions for more information.

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Feb 5, 2021
@rust-log-analyzer

This comment has been minimized.

@jyn514
Copy link
Member

jyn514 commented Feb 6, 2021

     Checking atty v0.2.14
error[E0463]: can't find crate for `core`

error: aborting due to previous error

atty needs to be built after core, not before. You may want to see how this is done for other crates like libc.

@jyn514 jyn514 added the T-libs Relevant to the library team, which will review and decide on the PR/issue. label Feb 6, 2021
@ghost
Copy link
Author

ghost commented Feb 7, 2021

     Checking atty v0.2.14
error[E0463]: can't find crate for `core`

error: aborting due to previous error

atty needs to be built after core, not before. You may want to see how this is done for other crates like libc.

Hey @jyn514! Thanks for the help. I've just made a PR to the atty repo: softprops/atty#45 to add the 'rustc-dep-of-std' feature to 'atty', using the same setup as 'cfg-if': https://github.com/alexcrichton/cfg-if/blob/main/Cargo.toml. Once this is added then it should compile just fine, seems to be working locally for me.

@Mark-Simulacrum
Copy link
Member

Hm, so I actually am not sure we want to do this, or at least, I'm a bit worried about adding a dependency to libtest which seems to have significantly more complicated logic without a better understanding of that logic and the reasons it exists (probably good ones!); I would also prefer to keep std/test dependencies to ones controlled by rust-lang so that we don't need to fork things or otherwise have trouble editing them. It is also my recollection that there are a number of different crates which do this, in which case it seems reasonable to ask why this one was chosen :)

cc @rust-lang/libs - can you give your opinion here?

@Mark-Simulacrum Mark-Simulacrum added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 12, 2021
@m-ou-se
Copy link
Member

m-ou-se commented Feb 12, 2021

I'd prefer to not add dependencies to the std/alloc/core/test/.. crates from outside the rust-lang. I wouldn't be opposed to adding a isatty() (is_terminal()? is_console()?) function to the standard library though.

@BurntSushi
Copy link
Member

Hm, so I actually am not sure we want to do this, or at least, I'm a bit worried about adding a dependency to libtest which seems to have significantly more complicated logic without a better understanding of that logic and the reasons it exists (probably good ones!);

I'm the one who wrote most of that more complicated logic and contributed it to the atty crate. I got it from how git does the same thing. The comments in the code of the atty crate explain most of it I think. The high level bit is that on Windows, detecting a tty differs depending on whether you're in a standard Windows console (such as cmd.exe) or whether you're in an MSYS terminal.

I would also prefer to keep std/test dependencies to ones controlled by rust-lang so that we don't need to fork things or otherwise have trouble editing them.

I don't have any strong opinions on this, but would tend to agree with you. Certainly, I employ this strategy in some cases with my own crates, so it makes sense to me.

It is also my recollection that there are a number of different crates which do this, in which case it seems reasonable to ask why this one was chosen :)

The isatty crate is deprecated in favor of atty. Maybe there are other tty detection crates that have atty's logic now, but atty was definitely the first. Given the simplicity of the API, I've never done a survey of the ecosystem since atty did what I wanted. But my sense is that atty is the de facto standard solution to the problem in the ecosystem today.


Other thoughts:

  1. It might be okay to just copy the logic from atty instead of bringing in the crate itself. The logic has been rather fixed and unchanged for a few years at this point. While some of the details are complex, the amount of code is very small.
  2. I would also be in favor of adding an isatty routine to std as well, although, the way in which the atty crate detects a tty on Windows is pretty non-standard. In particular, its cygwin/MSYS handling is mostly a heuristic. So whether that would be appropriate for a std API isn't quite clear to me. It depends on whether we can get away with documenting it as a platform specific heuristic that can't be relied on 100%.

@Amanieu
Copy link
Member

Amanieu commented Feb 12, 2021

I feel that this logic should probably be moved into libstd since we want Stdout to only be line buffered if we are printing to a tty.

// FIXME: this should be LineWriter or BufWriter depending on the state of
// stdout (tty or not). Note that if this is not line buffered it
// should also flush-on-panic or some form of flush-on-abort.
inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,

@ghost
Copy link
Author

ghost commented Apr 1, 2021

Coming back to this after getting some more Rust experience.

So basically we'd want to just take the logic that is currently in the atty crate and put it into std/io? If so, should that go in the stdio.rs file, in a new file, or somewhere else?

Would appreciate any help on this!

@Amanieu
Copy link
Member

Amanieu commented Apr 1, 2021

I feel that the best place for the logic is in library/std/src/sys/$TARGET/stdio.rs.

This then needs to be exported as an unstable feature in std's public API so that test can import it. Perhaps this could be a method on the Stdin/Stdout/Stderr types?

@ghost
Copy link
Author

ghost commented Apr 23, 2021

I feel that the best place for the logic is in library/std/src/sys/$TARGET/stdio.rs.

This then needs to be exported as an unstable feature in std's public API so that test can import it. Perhaps this could be a method on the Stdin/Stdout/Stderr types?

Hey @Amanieu , Had a first go at implementing the methods on all of the platforms that the atty crate supported (unix, windows, hermit) . I added an IsAtty trait to std::io that I just repeated on each of the three streams for each of the platforms.

I marked it all with an unstable feature is_atty and also had to mark everything that wasn't part of the feature as stable so the rest of the project still complied. Not sure if that was correct though, especially because I just put it as:
#[stable(feature = "rust1", since = "1.0.0")]
because I don't actually know when these were stabilized. I'm guessing it was pre-1.0 though.

Let me know what you think, I'm sure I did something wrong, lol.

@@ -516,6 +516,9 @@ pub mod task {
mod sys_common;
mod sys;

#[unstable(feature = "is_atty", issue = "80937")]
pub use sys::stdio::{Stderr, Stdin, Stdout};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

None of the items inside sys should be exported. This is solely an implementation detail. Instead is_atty should be on the Std* types inside std::io. You could add an IsAtty trait to each of the relevant platform modules inside the os module and implement it there. This should likely forward to a method on sys::stdio::Std* though. The os module is where platform specific extensions should be.

next_timeout - now
} else {
Duration::new(0, 0)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you have been formatting with a stable rustfmt that doesn't support some of the options specified in rustfmt.toml. Please use ./x.py fmt instead.

@@ -30,7 +31,7 @@ pub struct TestOpts {
impl TestOpts {
pub fn use_color(&self) -> bool {
match self.color {
ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
ColorConfig::AutoColor => !self.nocapture && Stdout::is_atty(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As written this will break all platforms for which there is no IsAtty implementation yet.

@ghost ghost closed this Apr 23, 2021
@jyn514
Copy link
Member

jyn514 commented Apr 23, 2021

@mattwilkinsonn did you mean to close this?

@ghost
Copy link
Author

ghost commented Apr 23, 2021

@mattwilkinsonn did you mean to close this?

No was reverting my commits so I can get back on the latest version of the repo. Will reopen once i have another revision

@ghost
Copy link
Author

ghost commented Apr 23, 2021

@bjorn3
Looking in std::os, seems like the std::os::windows crate is actually std::sys::windows::ext, just re-exported. I assume I should implement the trait in there? Same for unix & hermit.

@ghost ghost reopened this Apr 23, 2021
@bjorn3
Copy link
Member

bjorn3 commented Apr 23, 2021

Ah, yes. I looked at the linux specific mod which isn't a re-export of ...::ext.

@ghost ghost changed the title libtest: Use 'atty' crate io/os: Implement IsAtty trait on Stdin, Stdout, and Stderr Apr 24, 2021
@ghost ghost changed the title io/os: Implement IsAtty trait on Stdin, Stdout, and Stderr io/os: Implement IsAtty trait on Stdio Apr 24, 2021
@ghost
Copy link
Author

ghost commented Apr 24, 2021

@bjorn3

Ok, I implemented the trait inside the ext modules for each platform on the raw Stdout, Stdin, and Stderr, and then wrapped around those in io, using a cfg_if to prevent issues for the other platforms. Let me know what you think


/// Trait to determine if stdio stream (Stdin, Stdout, Stderr) is a tty.
#[unstable(feature = "is_atty", issue = "80937")]
pub trait IsAtty {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight nit: this is actually 3 words: is a TTY

So the proper name for the trait should be IsATty. But that looks pretty ugly so perhaps IsTerminal might be a better name.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed to IsTerminal and renamed the function and feature accordingly

@ghost ghost changed the title io/os: Implement IsAtty trait on Stdio io/os: Implement IsTerminal trait on Stdio Apr 24, 2021
@bors
Copy link
Contributor

bors commented May 5, 2021

☔ The latest upstream changes (presumably #84200) made this pull request unmergeable. Please resolve the merge conflicts.

@m-ou-se
Copy link
Member

m-ou-se commented Jul 5, 2021

I see this is still marked as draft. What work is there left to do before this is ready for review?

@bjorn3
Copy link
Member

bjorn3 commented Jul 5, 2021

There are compile error it seems.

Copy link
Contributor

@wooster0 wooster0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would really like to see this move forward. I think it'd be great to have this in the std.


if unsafe { console_on_any(&[fd]) } {
// False positives aren't possible. If we got a console then
// we definitely have a tty on stdin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// we definitely have a tty on stdin.
// we definitely have a tty on stdout.


if unsafe { console_on_any(&[fd]) } {
// False positives aren't possible. If we got a console then
// we definitely have a tty on stdin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// we definitely have a tty on stdin.
// we definitely have a tty on stderr.

use c::{
STD_ERROR_HANDLE as STD_ERROR, STD_INPUT_HANDLE as STD_INPUT,
STD_OUTPUT_HANDLE as STD_OUTPUT,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a possibility to just remove these 3 uses and use them directly like c::STD_ERROR_HANDLE etc. like it's done in other parts of this file? See e.g.

unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle }

@ghost
Copy link
Author

ghost commented Nov 10, 2021

Sorry, got distracted with other stuff for the past few months. I'll take a look at this again this week/next week. Thanks for the feedback!

@ghost
Copy link
Author

ghost commented Nov 21, 2021

I've been looking into making these updates, however it looks like my branch is far enough behind that I can't get the submodule artifacts in order to run any x.py commands.
image

In addition rebasing onto thousands of commits sounds a bit daunting. What would be the best approach for this? Just revert back to the HEAD and manually rewrite the changes into a new commit?

@Amanieu
Copy link
Member

Amanieu commented Nov 22, 2021

Considering the changes aren't too big (only ~250 lines changed) it's reasonable to attempt a manual rewrite if you can't get rebasing to work.

@ghost
Copy link
Author

ghost commented Nov 22, 2021

Closing as I've reimplemented the changes in #91121.

@ghost ghost closed this Nov 22, 2021
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use atty crate in libtest instead of rolling our own
10 participants